/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algoritmogeneticoCHC;


import java.util.*;
/*Esta clase corresponde a los individuos de un algoritmo genético. Como atributos
 * tiene: 
 *  ** Genoma
 *  ** Longitud del genoma
 *  ** Número de bits para enteros
 *  ** Número de bits para decimales
 *  ** Número de variables
 *  ** Fitness
 *  ** Fitness normalizado
 *  ** CDF
 *  ** Probabilidad de mutación
 *  ** Generador de números aleatorios
 * 
 * Como métodos tiene: 
 *  ** Creación del individuo (varias variantes)
 *  ** Impresión del individuo (varias variantes)
 *  ** Crossover
 *  ** Mutación puntual
 *  ** Función que calcula el fitness
 *  ** Función que normaliza el fitness (linear offset)*/
public class Individuo {
    
    public double fitness;
    public double fitnessNorm;
    public double cdf;
    public int[] genoma;
    public int longitud;
    public int bitsEnt;
    public int bitsDec;
    public int numVar;
    public double probMuta;
    public Random alea;
    
    public Individuo(){}
    
    // Constantes
    double Figment;     
    double Colossus;
    
    //Método que inicializa un individuo sin su genoma
    public Individuo(int bEnt, int bDec, int longi, int nV, double pM, Random al){
        bitsEnt = bEnt;
        bitsDec = bDec;
        numVar = nV;
        longitud = longi;
        genoma = new int[longitud];
        probMuta = pM;
        alea = al;
        Figment = -1000000000;
        Colossus = 1000000000;
    }
    
    //Método que inicializa a un individuo a partir de un genoma
    public Individuo(int[] g){
        genoma = g;
    }
    
    //Método que inicializa a un individuo copiándolo de otro individuo
    public Individuo(Individuo ind2){
      fitness = ind2.fitness;
      fitnessNorm = ind2.fitnessNorm;
      cdf = ind2.cdf;
      longitud = ind2.longitud;
      //genoma = ind2.genoma;
      genoma = new int[longitud];
      System.arraycopy(ind2.genoma, 0, genoma, 0, longitud);
      bitsEnt = ind2.bitsEnt;
      bitsDec = ind2.bitsDec;
      numVar = ind2.numVar;
      probMuta = ind2.probMuta;
      alea = ind2.alea;
    }
    
    //Método que imprime el genoma 
    public void ImprimeGenoma(){
        for(int i = 0; i< longitud; i++){
            System.out.print(""+genoma[i]);
        }
    }
    
    //Método que imprime toda la información del individuo
    public void ImprimeTodo(){
        System.out.println("Genoma: ");
        ImprimeGenoma();
        System.out.println("\nFitness: "+fitness);
        System.out.println("Fitness normalizado: "+fitnessNorm);
        System.out.println("CDF: "+cdf);
    }
    
    //Método que imprime los valores de las variables codificadas en el genoma del individuo
    public void ImprimeValores(){
        double valores[] = new double[numVar];
        valores = SeparaVariables();
        for(int i = 0; i< numVar; i++){
            System.out.println("Variable "+i+": "+valores[i]);
        }
    }
    
    public double[] RegresaValores(){
        double valores[] = new double[numVar];
        valores = SeparaVariables();
        return valores;
    }
    
    //Método que imprime el valor de las variables del genoma y el fitness 
    public void ImprimeResultado(){
        double valores[] = new double[numVar];
        System.out.println("El valor de la función objetivo es: "+fitness);
        valores = SeparaVariables();
        for(int i = 0; i< numVar; i++){
            System.out.println("Variable "+i+": "+valores[i]);
        }
    }
    
    public String RegresaOptimoString(){
        String optimo = "";
        optimo = optimo+""+fitness;
        /*double valores[] = new double[numVar];
        valores = SeparaVariables();
        for(int i = 0; i< numVar; i++){
            optimo= optimo+","+valores[i];
        }*/
        return optimo;
    }
    
    //Método que copia el genoma
    public void CopiaGenoma(Individuo ind2){
        genoma = new int[longitud];
        System.arraycopy(ind2.genoma, 0, genoma, 0, longitud);
    }
    
    //Método que crea aleatoriamente un genoma
    public void CreaAleatorio(){
        //genoma = new int[longitud];
        for(int i = 0; i<longitud; i++){
            double aleatorio = alea.nextDouble();
            if(aleatorio > .5){
                genoma[i]=1;
            }
            else{
                genoma[i]=0;
            }
        }        
    }
    
    //Método que crea un genoma de puros unos
    public void CreaUnos(){
        for(int i = 0; i < longitud; i++){
            genoma[i]=1;
        }
    }
    
    //Método que crea un genoma de puros ceros
    public void CreaCeros(){
        for(int i = 0; i<longitud; i++){
            genoma[i]=0;
        }
    }
    
    //Método que hace el crossover
    public Individuo XOver(Individuo ind2,int puntoCruce){
        //Se crea un nuevo individuo
        Individuo ind3 = new Individuo(bitsEnt, bitsDec, longitud, numVar, probMuta,alea);
        //La primera parte del genoma del nuevo individuo corresponde al 
        //individuo al que se le aplica el método
        System.arraycopy(genoma, 0, ind3.genoma, 0, puntoCruce);
        //La segunda parte del genoma pertenece al individuo que entra como parámetro
        System.arraycopy(ind2.genoma, puntoCruce, ind3.genoma, puntoCruce, longitud-puntoCruce);
        return ind3;
    }
    
    //Método que crea un vector con los índices de los bits que se van a mutar.
    //No permite que hayan elementos repetidos
    public void VectorPosiciones(int[] posiciones){
        boolean yaEstaba;
        int pos;
        int i = 0;
        while(i<posiciones.length){
            pos = (int)Math.floor(alea.nextDouble()*longitud);
            yaEstaba = false;
            for(int j = 0; j<i; j++){
                if(posiciones[j]==pos){
                    yaEstaba = true;
                }
            }
           if(!yaEstaba){
               posiciones[i]=pos;
               i++;
           }
        }
    }
    
    //Método que hace la mutación del individuo. Lo hace mediante el valor esperado
    //de mutación, no calcula un número aleatorio por cada bit.
    public void Muta(){
        /*Para que mutemos exactemente el número esperado de bits vamos a crear un 
         * vector de posiciones a mutar. En este vector no va a haber posiciones repetidas.
         */
        int posiciones[] = new int[(int)Math.floor(probMuta*longitud)];
        VectorPosiciones(posiciones);
        //Para cada posición que aparece en el vector posiciones, mutamos el bit correspondiente
        for(int i = 0; i < posiciones.length; i++ ){
            if(genoma[posiciones[i]]==1){
                genoma[posiciones[i]]=0;
            }
            else{
                genoma[posiciones[i]]=1;
            }
        }  
    }
    
    //Método que transforma un número binario a uno en base 10 con el número de 
    //bits para enteros y decimales especificados
    public double BinADecimal(int[] binario){
        double decimal = 0;
        int potencia = bitsEnt-1;
        for(int i = 1; i < binario.length; i++ ){
            decimal = binario[i]*Math.pow(2,potencia) + decimal;
            potencia--;
        }
        if(binario[0]==1){
            decimal = -decimal;
        }
        return decimal;
    }
    
    //Método que calcula la cdf
    public void CDF(double total){
        cdf = fitnessNorm/total;
    }
    
    //Método que calcula el fitness
    public void Fitness(int op){
        double variables[] = new double[numVar]; 
        switch (op){
            //La opción 1: corresponde a la función de fitness que se va a usar            
            case 1: variables = SeparaVariables();
                Final1(variables);
                break;
            case 2: variables = SeparaVariables();
                Final2(variables);
                break;
            case 3: variables = SeparaVariables();
                Final3(variables);
                break;
            case 4: variables = SeparaVariables();
                Final4(variables);
                break;
            case 5: variables = SeparaVariables();
                Final5(variables);
                break;
            case 6: variables = SeparaVariables();
                Final6(variables);
                break;
            case 7: variables = SeparaVariables();
                Final7(variables);
                break;
            case 8: variables = SeparaVariables();
                Final8(variables);
                break;
            case 9: variables = SeparaVariables();
                Final9(variables);
                break;
            case 10: variables = SeparaVariables();
                Final10(variables);
                break;
            case 11: variables = SeparaVariables();
                Final11(variables);
                break;
            case 12: variables = SeparaVariables();
                Final12(variables);
                break;
            case 13: variables = SeparaVariables();
                Final13(variables);
                break;
            case 14: variables = SeparaVariables();
                Final14(variables);
                break;
            case 15: variables = SeparaVariables();
                Final15(variables);
                break;
            case 16: variables = SeparaVariables();
                Tarea1(variables);
                break;
           // case 2: Deceptive(12,22);
                //break;
            case 17: variables = SeparaVariables();
                Clase(variables);
                break;
            case 18: variables = SeparaVariables();
                DeJong(variables);
                break;
            case 19: variables = SeparaVariables();
                Final1(variables);
                break;
        }
    }
    
    //Método que normaliza el fitness con linear offset
    public void LinearOffset(double minAbs, double mediaAbs){
        fitnessNorm = fitness + mediaAbs + minAbs;
    }
    
    //Método que encuentra el valor de las variables codificadas en el genoma
    public double[] SeparaVariables(){
        double valores[] = new double[numVar];
        int longVar = (int)(longitud/numVar);
        for(int i = 0; i < numVar; i++){
            int provisional[]= new int[longVar]; 
            System.arraycopy(genoma,i*longVar,provisional,0,longVar);
            valores[i] = BinADecimal(provisional);
        }
        return valores;
    }
    
    //Función de fitness para la Tarea 1.
    public void Tarea1(double variables[]){
        double castigo = -1000000000;
        int numRest = 4;
        int c = 0; //Indica el número de restricciones que sí se cumplen.
        if(2*variables[0]+3*variables[1]<=12){ //Restricción 1
            c++;
        }
        if(4*variables[0]+variables[1]<=8){ //Restricción 2
            c++;
        }
        if(variables[0]>=0){ //Restricción 3
            c++;
        }
        if(variables[1]>=0){ //Restricción 4
            c++;
        }
        if(c==4){
            fitness = 6*variables[0]+9*variables[1];
        }
        else{
            fitness = castigo + c*(castigo/numRest); //Regreso el total del castigo menos las que sí cumple
        }
    }
    
    public void DeJong(double variables[]){
        double fx=0;
        for(int i=0;i<variables.length; i++){
            fx = fx - variables[i]*variables[i];
        }
        fitness = fx;
    }
    
     private double Final1(double[] vars) {
        double X = vars[0];
        double Y = vars[1];
        
        return (X*X - (2*X*Y) + Y*Y*Y + X + Y - 2);
    }
    
    private double Final2(double[] vars) {
        double X = vars[0];
        
        return (X*X*X + 2.5*X*X - 2*X + 1);
    }

    private double Final3(double[] vars) {
        double X = vars[0];
        double Y = vars[1];
        
        return (-(X-5)*(X-5)-(Y-3)*(Y-3));
    }
    
    private double Final4(double[] vars) {
        double X = vars[0];
        double Y = vars[1];
        
        return (X*X - 2*X*Y + Y*Y*Y + X + Y - 2);
    }
    
    private double Final5(double[] vars) {
        double numRestrict = 2;
        double lilNum = Colossus;
        double k = lilNum/numRestrict;
        int c = 0;  //Num restrictions approved
        double X = vars[0];
        double Y = vars[1];
        
        if( (2*X + 3*Y - 13) >= 0) {
            c++;
        }
        if( (X - 2*Y + 4) >= 0) {
            c++;
        }
        
        return((c != numRestrict)? (lilNum-(k*c)):(3*X+Y-9));
    }
    
    private double Final6(double[] vars) {
        double numRestrict = 3;
        double lilNum = Colossus;
        double k = lilNum/numRestrict;
        int c = 0;  //Num restrictions approved
        double X = vars[0];
        double Y = vars[1];
        double Z = vars[2];
        
        if( (Math.sin(X) - 2*X*X - Y + 3.158529) >= 0) {
            c++;
        }
        if( (Math.cos(Y) + Y*Y - X + 2*Z - 0.583853) >= 0) {
            c++;
        }
        if( (X*X + Y*Y + Z*Z - 6) >= 0) {
            c++;
        }
        double resultEq = (Math.sin(X) - 2*X*X - Y + 3.158529)+(Math.cos(Y) + Y*Y - X + 2*Z - 0.583853)+(X*X + Y*Y + Z*Z - 6);
        return((c != numRestrict)? (lilNum-(k*c)):(resultEq));
    }
    
    private double Final7(double[] vars) {
        double numRestrict = 3;
        double lilNum = Colossus;
        double k = lilNum/numRestrict;
        int c = 0;  //Num restrictions approved
        double X = vars[0];
        double Y = vars[1];
        double Z = vars[2];
        //*Minimizar
		
        if( (X*X + 3*Y - Z*Z*Z + 10) >= 0) {
            c++;
        }
        if( (X*X + Y*Y +Z*Z -6) >= 0) {
            c++;
        }
        if( (X*X*X - Y*Y + Z -2) >= 0) {
            c++;
        }
        double resultEq = 3*Math.pow(X,2)+3*Y+Z+Math.pow(Z,2)-Math.pow(Z,3)+2;
        return((c != numRestrict)? (lilNum-(k*c)):(resultEq));
    }
    
    private double Final8(double[] vars) {
        double X = vars[0];
        
        return ((X*X*X*X) - (X*X*X) + (2.5*X*X) - (2*X) - 34.0625);
    }    
    
    private double Final9(double[] vars) {
        double X = vars[0];        
        double Y = vars[2];
        
        return ( ((1-X)*(1-X))+(100*(Y-X*X)*(Y-X*X)) );
    }
    
    private double Final10(double[] vars) {
        double numRestrict = 3;
        double lilNum = Figment;
        double k = lilNum/numRestrict;
        int c = 0;  //Num restrictions approved
        double X = vars[0];
        double Y = vars[1];
        if( 3*X + 4*Y <= 9 ) {
            c++;
        }
        if( 5*X + 2*Y>= 8) {
            c++;
        }
        if( X - 2*Y <= 1) {
            c++;
        }
        return((c != numRestrict)? (lilNum-(k*c)):(10*X + 11*Y));
    }
    
    private double Final11(double[] vars) {
        double numRestrict = 3;
        double lilNum = Figment;
        double k = lilNum/numRestrict;
        int c = 0;  //Num restrictions approved
        double X = vars[0];
        double Y = vars[1];
        
        if( 3*X + 4*Y <= 9 ) {
            c++;
        }
        if( 5*X + 2*Y <= 8) {
            c++;
        }
        if( X - 2*Y <= 1) {
            c++;
        }
        return((c != numRestrict)? (lilNum-(k*c)):(10*X + 11*Y));
    }
    
    private double Final12(double[] vars) {
        double numRestrict = 3;
        double lilNum = Figment;
        double k = lilNum/numRestrict;
        int c = 0;  //Num restrictions approved
        double X = vars[0];
        double Y = vars[1];
        double Z = vars[2];
        /*Minimizar*/
        if( 2*X - 2*Y + 3*Z-5 >=0 ) {
            c++;
        }
        if( X + Y + Z+2>= 0 ) {
            c++;
        }
        if( X- 2*Y - 5*Z+2 >= 0) {
            c++;
        }
        
        if(c != numRestrict) {
            return (lilNum-(k*c));
        }
        
        return(4*X - 3*Y - Z - 1);
    }
    
    private double Final13(double[] vars) {
        double numRestrict = 3;
        double lilNum = Figment;
        double k = lilNum/numRestrict;
        int c = 0;  //Num restrictions approved
        double X = vars[0];
        double Y = vars[1];
        double Z = vars[2];
        
        if( X*X + 3*Y - Z*Z*Z + 10 >= 0 ) {
            c++;
        }
        if( X*X + Y*Y + Z*Z - 6 >= 0) {
            c++;
        }
        if( X*X*X - Y*Y + Z - 2 >= 0) {
            c++;
        }
        
        if(c != numRestrict) {
            return (lilNum-(k*c));
        }
        
        return(2*X*X + Y*Y*Y + 3*Y + Z + Z*Z - Z*Z*Z + 8);
    }
    
    private double Final14(double[] vars) {
        int numVars = vars.length;
        double numRestrict = 2*numVars;
        double lilNum = Colossus;
        double k = lilNum/numRestrict;
        int c = 0;  //Num restrictions approved
        double result = 0;
        
        for(int varIndex=0; varIndex<numVars; varIndex++){
            if(vars[varIndex] <= 100){
                c++;
            }
        }
        for(int varIndex=0; varIndex<numVars; varIndex++){
            if(vars[varIndex] >= -100){
                c++;
            }
        }
        
        if(c != numRestrict) {
            result = lilNum-(k*c);
        } 
        else {
            for(int i=0; i < numVars; i++) {
                result += vars[i]*vars[i];
            }
        }
        
        return result;
    }
    
    private double Final15(double[] vars) {
        double numRestrict = 2;
        double lilNum = Figment;
        double k = lilNum/numRestrict;
        int c = 0;  //Num restrictions approved
        double X = vars[0];
        double Y = vars[1];
        double result;
        
        if( X > 0 ) {
            c++;
        }
        if( Y <= 1) {
            c++;
        }
        if(c != numRestrict) {
            result = lilNum-(k*c);
        }
        else {
            result = 0.497237718 
                - Y*Y
                + 0.58545564 * Y*Y*Y*Y*Y
                + 0.17568009
                - 0.141795785 * X * Y*Y*Y*Y*Y
                - 0.181941733 * X*X * Y*Y*Y*Y
                + 0.048105665 * X*X*X
                + .227004708* X*X*X*X * Y*Y
                + 1.964255192 * X*X*X*X * Y*Y*Y*Y*Y
                - 2.361551235* X*X*X*X*X * Y*Y*Y*Y*Y;
        }
        
        return(result);
    }
    
    //Función de fitness para la Tarea 1.
    public void Clase(double variables[]){
        double castigo = -1000000000;
        int numRest = 5;
        int c = 0; //Indica el número de restricciones que sí se cumplen.
        if(3*variables[0]+4*variables[1]<=9){ //Restricción 1
            c++;
        }
        if(5*variables[0]+2*variables[1]>=8){ //Restricción 2
            c++;
        }
        if(variables[0]-2*variables[1]<=1){ //Restricción 3
            c++;
        }
        if(variables[0]>=0){ //Restricción 4
            c++;
        }
        if(variables[1]>=0){ //Restricción 5
            c++;
        }
        if(c==numRest){
            fitness = 10*variables[0]+11*variables[1];
        }
        else{
            fitness = castigo + c*(castigo/numRest); //Regreso el total del castigo menos las que sí cumple
        }
        
    }
    
    public void ImprimeArreglo(int arreglo[]){
        for(int i = 0; i< arreglo.length; i++){
            System.out.print(""+arreglo[i]);
        }
    }
    
    public void Deceptive(int inicio, int salto){
        int suma = 0; //Contamos el número de unos en el genoma
        for(int i = 0; i<longitud; i++){
            suma = suma + genoma[i];
        }
        int cadena[] = new int[longitud];
        for(int i = 0; i<suma; i++){
            cadena[i]=1;
        }
        System.out.println("Imprimiendo cadena inicial");
        ImprimeArreglo(cadena);
        int cont = 0;
        int k;
        int cerosder;
        while(!Iguales(cadena)){
            k = 0;
            cerosder = 0;
            while(k<=longitud-2 && !(cadena[longitud-1-k]==0 && cadena[longitud-k-2]==1)){
                if(cadena[longitud-1-k]==0){
                    cerosder++;
                }
                k = k+1;
            }
         cadena[longitud-1-k] = 1;
         cadena[longitud - k-2] = 0;
         
         System.out.println("\nCerosDer: "+cerosder);
         System.out.println("\nk: "+k);
         if(cerosder == longitud - suma-1){
             for(int i = 0; i<longitud-suma-1; i++){
                 cadena[longitud-1-i]=0;
             }
             for(int i = longitud -suma -1; i<=k-1; i++){
                 cadena[longitud-1-i]=1;
             }
         }
         cont++;
         System.out.println("Genoma.equals(cadena): "+ Iguales(cadena));
        }
        System.out.println("Imprime cadena: ");
        ImprimeArreglo(cadena);
        int aux = 0;
        for(int i = 1; i<longitud-suma;i++ ){
            aux = aux + Combinaciones(longitud,i);
        }
        System.out.println("Contador: "+cont);
        fitness = (aux+cont)*(salto)+inicio;
        System.out.println("Fitness: "+fitness);
    }
   
    
    public int Combinaciones(int n, int k){
       int aux = 1, kfac = 1;
       for(int i = 1; i<=k; i++){
           aux = aux*(n-k+i);
           kfac = kfac*i;
       }
       return aux/kfac;
    }

    
    public boolean Iguales(int arreglo[]){
        boolean resp = true;
        for(int i = 0; i< arreglo.length; i++){
            if(arreglo[i]!=genoma[i]){
                resp = false;
            }
        }
        return resp;
    }
    
    //Método que va acumulando los fitnesses normalizados para calcular la cdf
    public double AcumulaCDF(double acumulado){
        cdf = cdf + acumulado;
        return cdf;
    }
    
    //Método que hace la selección proporcional. 
    //Regresa verdadero si el individuo ha sido seleccionado
    public boolean Selecciona(double aleatorio){
        boolean seleccionado;
        if(aleatorio <= cdf){
            seleccionado = true;
        }
        else{
            seleccionado = false;
        }
        return seleccionado;
    }
    
    //Método que compara dos individuos de acuerdo a su CDF.
    //Si el individuo al que le pertenece el método es mayor, regresa un valor >0
    //Si el individuo al que le pertenece el método es menor, regresa un valor <0
    //Si son iguales, regresa 0
    public double Compara(Individuo ind2){
        double resul;
        resul = cdf-ind2.cdf;
        return resul;
    }
    
    public double p(double val, int power) {
        return Math.pow(val, power);
    }
}
